﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace StasmTCPServer
{
    // State object for reading client data asynchronously
    public class StateObject
    {
        // Client  socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 1024;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.
        public StringBuilder content = new StringBuilder();
        public List<byte> sb = new List<byte>();
    }


    class Server
    {
        public static ManualResetEvent allDone = new ManualResetEvent(false);

        private Thread listenThread;
        private static string rootPath = "";
        public Server()
        {
            //IPAddress ipAddress = Dns.Resolve("localhost").AddressList[0];
            //this.tcpListener = new TcpListener(ipAddress, 9501);
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            Trace.WriteLine("Server Working", "Information");
            this.listenThread.Start();
        }
        private void ListenForClients()
        {
            // Data buffer for incoming data.
            byte[] bytes = new Byte[1024];

            // Establish the local endpoint for the socket.
            // The DNS name of the computer
            // running the listener is "host.contoso.com".
            IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
            IPAddress ipAddress = ipHostInfo.AddressList[0];
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

            // Create a TCP/IP socket.
            Socket listener = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);

            // Bind the socket to the local endpoint and listen for incoming connections.
            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(100);

                while (true)
                {
                    // Set the event to nonsignaled state.
                    allDone.Reset();

                    // Start an asynchronous socket to listen for connections.
                    Trace.WriteLine("Waiting for a connection...", "Information");
                    listener.BeginAccept(
                        new AsyncCallback(AcceptCallback),
                        listener);

                    // Wait until a connection is made before continuing.
                    allDone.WaitOne();
                }

            }
            catch (Exception e)
            {
                Trace.WriteLine(e.ToString(), "Information");
            }
        }
        public void AcceptCallback(IAsyncResult ar)
        {
            // Signal the main thread to continue.
            allDone.Set();

            // Get the socket that handles the client request.
            Socket listener = (Socket)ar.AsyncState;
            Socket handler = listener.EndAccept(ar);

            // Create the state object.
            StateObject state = new StateObject();
            state.workSocket = handler;
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReadCallback), state);
        }
        public void ReadCallback(IAsyncResult ar)
        {
            String content = String.Empty;

            // Retrieve the state object and the handler socket
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            Socket handler = state.workSocket;

            // Read data from the client socket. 
            int bytesRead = handler.EndReceive(ar);

            if (bytesRead > 0)
            {
                state.content.Append(Encoding.ASCII.GetString(
                   state.buffer, 0, bytesRead));
                content = state.sb.ToString();

                string eof = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
                // There  might be more data, so store the data received so far.
                int indexEOF = eof.IndexOf("<EOF>");
                if (eof.IndexOf("<EOF>") > -1)
                {
                    for (int i = 0; i < indexEOF; i++)
                    {
                        state.sb.Add(state.buffer[i]);
                    }
                }
                else
                {
                    for (int i = 0; i < bytesRead; i++)
                    {
                        state.sb.Add(state.buffer[i]);
                    }
                }
                if (eof.IndexOf("<EOF>") > -1)
                {
                    // All the data has been read from the 
                    // client. Display it on the console.
                    byte[] byteData = state.sb.ToArray();
                    byte[] imagePathLen = new byte[sizeof(int)];
                    Array.Copy(byteData, imagePathLen, imagePathLen.Length);
                    byte[] imagePath = new byte[BitConverter.ToInt32(imagePathLen, 0)];
                    for (int i = 0; i < imagePath.Length; i++)
                    {
                        imagePath[i] = byteData[imagePathLen.Length + i];
                    }
                    string imgPath = Encoding.ASCII.GetString(imagePath, 0, imagePath.Length);
                    byte[] data = new byte[byteData.Length - (imagePath.Length + imagePathLen.Length)];
                    for (int i = 0; i < data.Length; i++)
                    {
                        data[i] = byteData[(imagePath.Length + imagePathLen.Length) + i];
                    }
                    Random rnd = new Random();
                    string imageName = Path.GetFileNameWithoutExtension(imgPath) + rnd.Next().ToString() + state.GetHashCode().ToString();
                    string imageNameWithExt = imageName + ".jpg";
                    string imageLongPath = Path.Combine(Environment.GetEnvironmentVariable("RoleRoot") + @"\", @"approot\StasmApp\OImage\" + imageNameWithExt);

                    File.WriteAllBytes(imageLongPath, data);
                    MakeFace(imageLongPath);
                    string svgNameWithExt = imageName + ".svg";
                    string svgLongPath = Path.Combine(Environment.GetEnvironmentVariable("RoleRoot") + @"\", @"approot\StasmApp\OImage\" + svgNameWithExt);
                    byte[] svgData = File.ReadAllBytes(svgLongPath);
                    Send(handler, svgData, imageNameWithExt);

                   
                }
                else
                {
                    // Not all data received. Get more.
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
                }
            }

      
        }
        private bool exited = false;
        public void MakeFace(string imagePath)
        {
            exited = false;
            object locker = new object();
            rootPath = Environment.GetEnvironmentVariable("RoleRoot");
            string sDataDir = String.Format("{0}\\approot\\StasmApp\\StasmData\\", rootPath);

            Process p = new Process();
            p.EnableRaisingEvents = true;
            p.StartInfo.FileName = "StasmApp\\StasmApp.exe";
            p.StartInfo.Arguments = "svg " + imagePath + " " + sDataDir;
            p.StartInfo.CreateNoWindow = true;
            p.Exited += new EventHandler(p_Exited);
            p.Start();
            p.WaitForExit();
            p.Close();
            File.Delete(imagePath);
        }

        void p_Exited(object sender, EventArgs e)
        {
            exited = true;
        }
        private static void Send(Socket handler, byte[] data, string imgPath)
        {
            // Begin sending the data to the remote device.
            byte[] eof = Encoding.ASCII.GetBytes("<EOF>");
            byte[] imagePathLen = BitConverter.GetBytes(imgPath.Length);
            byte[] imagePath = Encoding.ASCII.GetBytes(imgPath);
            byte[] byteData = new byte[imagePathLen.Length + imagePath.Length + data.Length + eof.Length];
            imagePathLen.CopyTo(byteData, 0);
            imagePath.CopyTo(byteData, imagePathLen.Length);
            data.CopyTo(byteData, imagePathLen.Length + imagePath.Length);
            eof.CopyTo(byteData, imagePathLen.Length + imagePath.Length + data.Length);

            // Begin sending the data to the remote device.
            handler.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), handler);
        }

        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket handler = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.
                int bytesSent = handler.EndSend(ar);
                Console.WriteLine("Sent {0} bytes to client.", bytesSent);

                handler.Shutdown(SocketShutdown.Both);
                handler.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}
